<?php
declare (strict_types=1);

namespace app\service\logic\common\user;

use app\exception\LogicException;
use app\model\communal\InviteModel;
use app\model\communal\UserStatisticsModel;
use app\service\logic\BaseService;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
use think\facade\Db;
use think\facade\Log;
use think\Model;

final class StatisticsService extends BaseService
{
    protected string $modelClassName = 'UserStatisticsModel';

    /**
     * 查找单挑记录可能为空
     *
     * @param $params
     * @return UserStatisticsModel|array|mixed|Model
     */
    public function getSimpleOrEmpty($params)
    {
        return UserStatisticsModel::findOrEmpty($params['id']);
    }

    /**
     * 获取可用元心币
     *
     * @param $userId
     * @return mixed|string
     */
    public function getCoinAvailableCountByUserId($userId)
    {
        $coinAvailableCount = '0';
        $model = UserStatisticsModel::findOrEmpty($userId);
        if (!$model->isEmpty()) {
            $coinAvailableCount = $model['coin_available_count'];
        }

        return $coinAvailableCount;
    }

    /**
     * 获取元心币相关
     *
     * @param $userId
     * @return mixed|string
     */
    public function getCoinByUserId($userId)
    {
        $accrueCoinCount = '0';
        $coinCount = '0';
        $coinAvailableCount = '0';
        $coinFreezeCount = '0';
        $model = UserStatisticsModel::findOrEmpty($userId);
        if (!$model->isEmpty()) {
            $coinCount = $model['coin_count'];
            $coinAvailableCount = $model['coin_available_count'];
            $coinFreezeCount = $model['coin_freeze_count'];
            $accrueCoinCount = $model['accrue_coin'];
        }

        return [
            'coin_count' => $coinCount, // 元心币当前总量
            'coin_available_count' => $coinAvailableCount, // 可用元心币
            'coin_freeze_count' => $coinFreezeCount, // 冻结元心币
            'accrue_coin' => $accrueCoinCount, // 累积元心币
        ];
    }

    /**
     * 锁定可用元心币
     *
     * @param $userId
     * @param $coinCount
     * @return bool
     * @throws LogicException
     * @throws DataNotFoundException
     * @throws ModelNotFoundException
     */
    public function freezeCoin($userId, $coinCount)
    {
        $model = $this->getSimpleDetail(['id' => $userId]);

        if ($coinCount > $model['coin_available_count']) {
            throw new LogicException('可用元心币不足');
        }

        $availableCount = bcsub($model['coin_available_count'], $coinCount, 2);
        $freezeCount = bcadd($model['coin_freeze_count'], $coinCount, 2);
        return $model->save(['coin_available_count' => $availableCount, 'coin_freeze_count' => $freezeCount]);
    }

    /**
     * 解冻元心币
     *
     * @param $userId
     * @param $coinCount
     * @return bool
     * @throws DataNotFoundException
     * @throws LogicException
     * @throws ModelNotFoundException
     */
    public function unfreezeCoin($userId, $coinCount)
    {
        $model = $this->getSimpleDetail(['id' => $userId]);

        if ($coinCount > $model['coin_freeze_count']) {
            throw new LogicException('锁定元心币不足');
        }

        $availableCount = bcadd($model['coin_available_count'], $coinCount, 2);
        $freezeCount = bcsub($model['coin_freeze_count'], $coinCount, 2);
        return $model->save(['coin_available_count' => $availableCount, 'coin_freeze_count' => $freezeCount]);
    }

    /**
     * 消耗冻结元心币&&产生元心币明细
     *
     * @param $userId
     * @param $coinCount
     * @param $coinType
     * @throws LogicException
     */
    public function consumeFreezeCoin($userId, $coinCount, $coinType)
    {
        $model = UserStatisticsModel::findOrEmpty($userId);
        if ($coinCount > 0 && !$model->isEmpty()) {
            if ($coinCount > $model['coin_freeze_count']) {
                throw new LogicException('锁定元心币不足');
            }

            $coinTypeMap = [2 => '消费', 3 => '提现'];
            $freezeCount = bcsub($model['coin_freeze_count'], $coinCount, 2);
            $totalCoinCount = bcadd($model['coin_available_count'], $freezeCount, 2);
            $model->save(['coin_count' => $totalCoinCount, 'coin_freeze_count' => $freezeCount]);
            invoke(CoinService::class)->create([
                'user_id' => $userId,
                'coin_count' => $coinCount,
                'coin_balance' => $totalCoinCount,
                'remark' => $coinTypeMap[$coinType] ?? '',
                'type' => $coinType
            ]);
        }
    }

    /**
     * 用户消费累积 并 返点邀请人
     *
     * @param $userId
     * @param $costAmount
     */
    public function accrueCost($userId, $costAmount)
    {
        Log::debug('accrue_cost: userId=' . $userId . ', costAmount=' . $costAmount);
        if ($costAmount > 0) {
            Db::connect('communal')->transaction(function () use ($userId, $costAmount) {
                $costAmount = (string)$costAmount;
                $model = UserStatisticsModel::findOrEmpty($userId);
                // 产生消费
                // 累积消费
                if ($model->isEmpty()) {
                    // 空空空
                    $model->save(['user_id' => $userId, 'accrue_cost' => $costAmount]);
                    $accrueCost = 0; // 单用户累积消费
                } else {
                    $accrueCost = bcadd($model['accrue_cost'], $costAmount, 2); // 单用户累积消费
                    $model->save(['accrue_cost' => $accrueCost]);
                }

                // 邀请人返元心币
                $from = InviteModel::where('to', $userId)->value('from');
                Log::debug('accrue_cost: userId=' . $userId . ', costAmount=' . $costAmount . ', from=' . $from);
                if ($from) {
                    // 获取该邀请人的所有被邀请人的累积金额
                    $allAccrueCost = invoke(InviteService::class)->getAccrueCostByFrom($from);
                    // 获取需要返点的元心币
                    $coin = $this->getCoinByAccrueCost($allAccrueCost, $costAmount);
                    invoke(CoinService::class)->back(['user_id' => $from, 'coin_count' => $coin, 'type' => 1]);
                }
            });
        }
    }

    /**
     * 根据消费获取元心币(简简单单cp)
     *
     * @param $accrueCost
     * @param $cost
     * @return string
     */
    public function getCoinByAccrueCost($accrueCost, $cost)
    {
        // dd($accrueCost, $cost);
        static $coin = '0';
        $accrueCost = (string)$accrueCost;
        $cost = (string)$cost;
        if ($accrueCost >= 0 && $accrueCost < 1000) {
            // 计算用量
            $mark = bcsub('1000', (string)$accrueCost, 2);
            // dd($mark, $cost);
            if ($mark >= $cost) {
                $consume = $cost;
                $coin = bcadd($coin, bcmul($consume, '0.05', 2), 2);
            } else {
                $consume = $mark;
                $coin = bcadd($coin, bcmul($consume, '0.05', 2), 2);
                $cost = bcsub($cost, $mark, 2);
                // dd($cost);
                $this->getCoinByAccrueCost('1000', $cost);
            }
        } elseif ($accrueCost >= 1000 && $accrueCost < 5000) {
            // 计算用量
            $mark = bcsub('5000', (string)$accrueCost, 2);
            if ($mark >= $cost) {
                $consume = $cost;
                $coin = bcadd($coin, bcmul($consume, '0.07', 2), 2);
            } else {
                $consume = $mark;
                $coin = bcadd($coin, bcmul($consume, '0.07', 2), 2);
                $cost = bcsub($cost, $mark, 2);
                $this->getCoinByAccrueCost('5000', $cost);
            }
        } elseif ($accrueCost >= 5000 && $accrueCost < 10000) {
            $mark = bcsub('10000', (string)$accrueCost, 2);
            if ($mark >= $cost) {
                $consume = $cost;
                $coin = bcadd($coin, bcmul($consume, '0.1', 2), 2);
            } else {
                $consume = $mark;
                $coin = bcadd($coin, bcmul($consume, '0.1', 2), 2);
                $cost = bcsub($cost, $mark, 2);
                $this->getCoinByAccrueCost('10000', $cost);
            }
        } elseif ($accrueCost >= 10000 && $accrueCost < 30000) {
            $mark = bcsub('30000', (string)$accrueCost, 2);
            if ($mark >= $cost) {
                $consume = $cost;
                $coin = bcadd($coin, bcmul($consume, '0.12', 2), 2);
            } else {
                $consume = $mark;
                $coin = bcadd($coin, bcmul($consume, '0.12', 2), 2);
                $cost = bcsub($cost, $mark, 2);
                $this->getCoinByAccrueCost('30000', $cost);
            }
        } else {
            $consume = $cost;
            $coin = bcadd($coin, bcmul($consume, '0.15', 2), 2);
        }

        // 转为元心币
        $conversionRate = config('logic.coin_conversion_rate');
        return bcmul($coin, $conversionRate, 2);
    }
}
